package com.hlkj.warboot.configurer.security.authentication.provider;

import com.hlkj.warboot.configurer.properties.RsaProperties;
import com.hlkj.warboot.configurer.security.SecurityUser;
import com.hlkj.warboot.configurer.util.RSAUtil;
import com.hlkj.warboot.configurer.util.ResponseUtil;
import com.hlkj.warboot.configurer.util.enums.OperateEnum;
import com.hlkj.warboot.module_sys.entity.AdminEntity;
import com.hlkj.warboot.module_sys.service.AdminService;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.text.MessageFormat;
import java.util.Date;
import java.util.Objects;

/**
 * @Description TODO 用户点击登陆后的回调--执行登陆认证
 * @Author 李星
 * @Date 2019/6/29 22:52
 */
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
    @Resource
    private MyUserDetailsService myUserDetailsService;

    @Resource
    private RsaProperties rsaProperties;

    @Resource
    private AdminService adminService;

    /*
     * @Description 检查入参authentication是否是UsernamePasswordAuthenticationToken或它的子类
     * @Author 李星
     * @Date 2019/6/3 20:28
     * @Param [authentication]
     * @return boolean
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }

    /*
     * @Description TODO 登陆认证
     * @Author 李星
     * @Date 2019/5/30 22:20
     * @Param [authentication 未认证Authentication对象]
     * @return 认证成功的Authentication对象
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // TODO 登陆相关参数（默认ip地址、默认sessionId、附加验证码等等）
        MyWebAuthenticationDetails myWebAuthenticationDetails = (MyWebAuthenticationDetails) authentication.getDetails();
        String remoteAddress = myWebAuthenticationDetails.getRemoteAddress(); // IP地址
        String sessionId = myWebAuthenticationDetails.getSessionId(); // sessionId
        String verifyCode = myWebAuthenticationDetails.getVerifyCode(); // 验证码

        // TODO 验证码校验
        // 获取HttpServletRequest对象
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        //HttpServletResponse response = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
        HttpSession session = request.getSession();
        Object verifyCodeSession = session.getAttribute(OperateEnum.VERIFYCODE.getValue());
        if (verifyCodeSession == null) {
            throw new BadCredentialsException("验证码失效");
        }
        if (!verifyCodeSession.toString().equalsIgnoreCase(verifyCode)) {
            throw new BadCredentialsException("验证码错误");
        }
        session.removeAttribute(OperateEnum.VERIFYCODE.getValue());

        // TODO 获取浏览器输入的账号和密码
        String username = authentication.getName(); // 账号
        String password = (String) authentication.getCredentials(); // 密码

        /**
         * @Description TODO 根据账号查询管理员信息，成功后的信息包含当前管理员的基本信息和权限信息
         *
         */
        // UserDetails userDetails = myUserDetailsService.loadUserByUsername(username);
        SecurityUser securityUser = (SecurityUser) myUserDetailsService.loadUserByUsername(username);

        // 账号是否存在
        if (securityUser == null) {
            throw new UsernameNotFoundException("账号不存在");
        }

        // 账号状态校验
        if (!securityUser.isAccountNonLocked()) {
            throw new LockedException("账号已被禁用");
        }

        // 密码校验
        String password_1 = securityUser.getPassword();
        try {
            password = RSAUtil.decryptByPrivateKey(rsaProperties.getPrivateKey(), password); // 前台公钥加密，后台私钥解密
            password_1 = RSAUtil.decryptByPrivateKey(rsaProperties.getPrivateKey(), password_1); // 前台公钥加密，后台私钥解密
        } catch (Exception e) {
            throw new BadCredentialsException("密码校验出错");
        }
        if (!password.equals(password_1)) {
            throw new BadCredentialsException("密码不正确");
        }


        // TODO 认证成功，更新用户的登陆状态信息
        AdminEntity userEntity = new AdminEntity();
        userEntity.setId(securityUser.getId());
        userEntity.setLoginIp(remoteAddress);
        userEntity.setLoginTime(new Date());
        try {
            adminService.updateById(userEntity);
        } catch (Exception e) {
            ResponseUtil.fail(MessageFormat.format("用户{0}登陆成功，更新登陆信息出错：{1}", username, e.getMessage()));
        }

        // TODO 认证成功，返回包含用户信息的UsernamePasswordAuthenticationToken实例对象
        return new UsernamePasswordAuthenticationToken(securityUser, password, securityUser.getAuthorities());
    }
}
